// Copyright (C) 2003, International Business Machines
// Corporation and others.  All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).

/* Pdco algorithm

Method


*/

#include "CoinPragma.hpp"

#include <math.h>

#include "CoinDenseVector.hpp"
#include "ClpPdco.hpp"
#include "ClpPdcoBase.hpp"
#include "CoinHelperFunctions.hpp"
#include "ClpHelperFunctions.hpp"
#include "ClpLsqr.hpp"
#include "CoinTime.hpp"
#include "ClpMessage.hpp"
#include <cfloat>
#include <cassert>
#include <string>
#include <stdio.h>
#include <iostream>

int ClpPdco::pdco()
{
  printf("Dummy pdco solve\n");
  return 0;
}
// ** Temporary version
int ClpPdco::pdco(ClpPdcoBase *stuff, Options &options, Info &info, Outfo &outfo)
{
  //    D1, D2 are positive-definite diagonal matrices defined from d1, d2.
  //           In particular, d2 indicates the accuracy required for
  //           satisfying each row of Ax = b.
  //
  // D1 and D2 (via d1 and d2) provide primal and dual regularization
  // respectively.  They ensure that the primal and dual solutions
  // (x,r) and (y,z) are unique and bounded.
  //
  // A scalar d1 is equivalent to d1 = ones(n,1), D1 = diag(d1).
  // A scalar d2 is equivalent to d2 = ones(m,1), D2 = diag(d2).
  // Typically, d1 = d2 = 1e-4.
  // These values perturb phi(x) only slightly  (by about 1e-8) and request
  // that A*x = b be satisfied quite accurately (to about 1e-8).
  // Set d1 = 1e-4, d2 = 1 for least-squares problems with bound constraints.
  // The problem is then
  //
  //    minimize    phi(x) + 1/2 norm(d1*x)^2 + 1/2 norm(A*x - b)^2
  //    subject to  bl <= x <= bu.
  //
  // More generally, d1 and d2 may be n and m vectors containing any positive
  // values (preferably not too small, and typically no larger than 1).
  // Bigger elements of d1 and d2 improve the stability of the solver.
  //
  // At an optimal solution, if x(j) is on its lower or upper bound,
  // the corresponding z(j) is positive or negative respectively.
  // If x(j) is between its bounds, z(j) = 0.
  // If bl(j) = bu(j), x(j) is fixed at that value and z(j) may have
  // either sign.
  //
  // Also, r and y satisfy r = D2 y, so that Ax + D2^2 y = b.
  // Thus if d2(i) = 1e-4, the i-th row of Ax = b will be satisfied to
  // approximately 1e-8.  This determines how large d2(i) can safely be.
  //
  //
  // EXTERNAL FUNCTIONS:
  // options         = pdcoSet;                  provided with pdco.m
  // [obj,grad,hess] = pdObj( x );               provided by user
  //               y = pdMat( name,mode,m,n,x ); provided by user if pdMat
  //                                             is a string, not a matrix
  //
  // INPUT ARGUMENTS:
  // pdObj      is a string containing the name of a function pdObj.m
  //            or a function_handle for such a function
  //            such that [obj,grad,hess] = pdObj(x) defines
  //            obj  = phi(x)              : a scalar,
  //            grad = gradient of phi(x)  : an n-vector,
  //            hess = diag(Hessian of phi): an n-vector.
  //         Examples:
  //            If phi(x) is the linear function c"x, pdObj should return
  //               [obj,grad,hess] = [c"*x, c, zeros(n,1)].
  //            If phi(x) is the entropy function E(x) = sum x(j) log x(j),
  //               [obj,grad,hess] = [E(x), log(x)+1, 1./x].
  // pdMat      may be an ifexplicit m x n matrix A (preferably sparse!),
  //            or a string containing the name of a function pdMat.m
  //            or a function_handle for such a function
  //            such that y = pdMat( name,mode,m,n,x )
  //            returns   y = A*x (mode=1)  or  y = A"*x (mode=2).
  //            The input parameter "name" will be the string pdMat.
  // b          is an m-vector.
  // bl         is an n-vector of lower bounds.  Non-existent bounds
  //            may be represented by bl(j) = -Inf or bl(j) <= -1e+20.
  // bu         is an n-vector of upper bounds.  Non-existent bounds
  //            may be represented by bu(j) =  Inf or bu(j) >=  1e+20.
  // d1, d2     may be positive scalars or positive vectors (see above).
  // options    is a structure that may be set and altered by pdcoSet
  //            (type help pdcoSet).
  // x0, y0, z0 provide an initial solution.
  // xsize, zsize are estimates of the biggest x and z at the solution.
  //            They are used to scale (x,y,z).  Good estimates
  //            should improve the performance of the barrier method.
  //
  //
  // OUTPUT ARGUMENTS:
  // x          is the primal solution.
  // y          is the dual solution associated with Ax + D2 r = b.
  // z          is the dual solution associated with bl <= x <= bu.
  // inform = 0 if a solution is found;
  //        = 1 if too many iterations were required;
  //        = 2 if the linesearch failed too often.
  // PDitns     is the number of Primal-Dual Barrier iterations required.
  // CGitns     is the number of Conjugate-Gradient  iterations required
  //            if an iterative solver is used (LSQR).
  // time       is the cpu time used.
  //----------------------------------------------------------------------

  // PRIVATE FUNCTIONS:
  //    pdxxxbounds
  //    pdxxxdistrib
  //    pdxxxlsqr
  //    pdxxxlsqrmat
  //    pdxxxmat
  //    pdxxxmerit
  //    pdxxxresid1
  //    pdxxxresid2
  //    pdxxxstep
  //
  // GLOBAL VARIABLES:
  //    global pdDDD1 pdDDD2 pdDDD3
  //
  //
  // NOTES:
  // The matrix A should be reasonably well scaled: norm(A,inf) =~ 1.
  // The vector b and objective phi(x) may be of any size, but ensure that
  // xsize and zsize are reasonably close to norm(x,inf) and norm(z,inf)
  // at the solution.
  //
  // The files defining pdObj  and pdMat
  // must not be called Fname.m or Aname.m!!
  //
  //
  // AUTHOR:
  //    Michael Saunders, Systems Optimization Laboratory (SOL),
  //    Stanford University, Stanford, California, USA.
  //    saunders@stanford.edu
  //
  // CONTRIBUTORS:
  //    Byunggyoo Kim, SOL, Stanford University.
  //    bgkim@stanford.edu
  //
  // DEVELOPMENT:
  // 20 Jun 1997: Original version of pdsco.m derived from pdlp0.m.
  // 29 Sep 2002: Original version of pdco.m  derived from pdsco.m.
  //              Introduced D1, D2 in place of gamma*I, delta*I
  //              and allowed for general bounds bl <= x <= bu.
  // 06 Oct 2002: Allowed for fixed variabes: bl(j) = bu(j) for any j.
  // 15 Oct 2002: Eliminated some work vectors (since m, n might be LARGE).
  //              Modularized residuals, linesearch
  // 16 Oct 2002: pdxxx..., pdDDD... names rationalized.
  //              pdAAA eliminated (global copy of A).
  //              Aname is now used directly as an ifexplicit A or a function.
  //              NOTE: If Aname is a function, it now has an extra parameter.
  // 23 Oct 2002: Fname and Aname can now be function handles.
  // 01 Nov 2002: Bug fixed in feval in pdxxxmat.
  //-----------------------------------------------------------------------

  //  global pdDDD1 pdDDD2 pdDDD3
  double inf = 1.0e30;
  double eps = 1.0e-15;
  double atolold = -1.0, r3ratio = -1.0, Pinf, Dinf, Cinf, Cinf0;

  printf("\n   --------------------------------------------------------");
  printf("\n   pdco.m                            Version of 01 Nov 2002");
  printf("\n   Primal-dual barrier method to minimize a convex function");
  printf("\n   subject to linear constraints Ax + r = b,  bl <= x <= bu");
  printf("\n   --------------------------------------------------------\n");

  int m = numberRows_;
  int n = numberColumns_;
  bool ifexplicit = true;

  CoinDenseVector< double > b(m, rhs_);
  CoinDenseVector< double > x(n, x_);
  CoinDenseVector< double > y(m, y_);
  CoinDenseVector< double > z(n, dj_);
  //delete old arrays
  delete[] rhs_;
  delete[] x_;
  delete[] y_;
  delete[] dj_;
  rhs_ = NULL;
  x_ = NULL;
  y_ = NULL;
  dj_ = NULL;

  // Save stuff so available elsewhere
  pdcoStuff_ = stuff;

  double normb = b.infNorm();
  double normx0 = x.infNorm();
  double normy0 = y.infNorm();
  double normz0 = z.infNorm();

  printf("\nmax |b | = %8g     max |x0| = %8g", normb, normx0);
  printf("      xsize   = %8g", xsize_);
  printf("\nmax |y0| = %8g     max |z0| = %8g", normy0, normz0);
  printf("      zsize   = %8g", zsize_);

  //---------------------------------------------------------------------
  // Initialize.
  //---------------------------------------------------------------------
  //true   = 1;
  //false  = 0;
  //zn     = zeros(n,1);
  //int nb     = n + m;
  int CGitns = 0;
  int inform = 0;
  //---------------------------------------------------------------------
  //  Only allow scalar d1, d2 for now
  //---------------------------------------------------------------------
  /*
     if (d1_->size()==1)
         d1_->resize(n, d1_->getElements()[0]);  // Allow scalar d1, d2
     if (d2_->size()==1)
         d2->resize(m, d2->getElements()[0]);  // to mean dk * unit vector
      */
  assert(stuff->sizeD1() == 1);
  double d1 = stuff->getD1();
  double d2 = stuff->getD2();

  //---------------------------------------------------------------------
  // Grab input options.
  //---------------------------------------------------------------------
  int maxitn = options.MaxIter;
  double featol = options.FeaTol;
  double opttol = options.OptTol;
  double steptol = options.StepTol;
  int stepSame = 1; /* options.StepSame;   // 1 means stepx == stepz */
  double x0min = options.x0min;
  double z0min = options.z0min;
  double mu0 = options.mu0;
  int LSproblem = options.LSproblem; // See below
  int LSmethod = options.LSmethod; // 1=Cholesky    2=QR    3=LSQR
  int itnlim = options.LSQRMaxIter * std::min(m, n);
  double atol1 = options.LSQRatol1; // Initial  atol
  double atol2 = options.LSQRatol2; // Smallest atol,unless atol1 is smaller
  double conlim = options.LSQRconlim;
  //int  wait      = options.wait;

  // LSproblem:
  //  1 = dy          2 = dy shifted, DLS
  // 11 = s          12 =  s shifted, DLS    (dx = Ds)
  // 21 = dx
  // 31 = 3x3 system, symmetrized by Z^{1/2}
  // 32 = 2x2 system, symmetrized by X^{1/2}

  //---------------------------------------------------------------------
  // Set other parameters.
  //---------------------------------------------------------------------
  int kminor = 0; // 1 stops after each iteration
  double eta = 1e-4; // Linesearch tolerance for "sufficient descent"
  double maxf = 10; // Linesearch backtrack limit (function evaluations)
  double maxfail = 1; // Linesearch failure limit (consecutive iterations)
  double bigcenter = 1e+3; // mu is reduced if center < bigcenter.

  // Parameters for LSQR.
  double atolmin = eps; // Smallest atol if linesearch back-tracks
  double btol = 0; // Should be small (zero is ok)
  double show = false; // Controls lsqr iteration log
  /*
     double gamma     = d1->infNorm();
     double delta     = d2->infNorm();
     */
  double gamma = d1;
  double delta = d2;

  printf("\n\nx0min    = %8g     featol   = %8.1e", x0min, featol);
  printf("      d1max   = %8.1e", gamma);
  printf("\nz0min    = %8g     opttol   = %8.1e", z0min, opttol);
  printf("      d2max   = %8.1e", delta);
  printf("\nmu0      = %8.1e     steptol  = %8g", mu0, steptol);
  printf("     bigcenter= %8g", bigcenter);

  printf("\n\nLSQR:");
  printf("\natol1    = %8.1e     atol2    = %8.1e", atol1, atol2);
  printf("      btol    = %8.1e", btol);
  printf("\nconlim   = %8.1e     itnlim   = %8d", conlim, itnlim);
  printf("      show    = %8g", show);

  // LSmethod  = 3;  ////// Hardwire LSQR
  // LSproblem = 1;  ////// and LS problem defining "dy".
  /*
       if wait
         printf("\n\nReview parameters... then type "return"\n")
         keyboard
       end
     */
  if (eta < 0)
    printf("\n\nLinesearch disabled by eta < 0");

  //---------------------------------------------------------------------
  // All parameters have now been set.
  //---------------------------------------------------------------------
  double time = CoinCpuTime();
  //bool useChol = (LSmethod == 1);
  //bool useQR   = (LSmethod == 2);
  bool direct = (LSmethod <= 2 && ifexplicit);
  char solver[7];
  strcpy(solver, "  LSQR");

  //---------------------------------------------------------------------
  // Categorize bounds and allow for fixed variables by modifying b.
  //---------------------------------------------------------------------

  int nlow, nupp, nfix;
  int *bptrs[3] = { 0 };
  getBoundTypes(&nlow, &nupp, &nfix, bptrs);
  int *low = bptrs[0];
  int *upp = bptrs[1];
  int *fix = bptrs[2];

  int nU = n;
  if (nupp == 0)
    nU = 1; //Make dummy vectors if no Upper bounds

  //---------------------------------------------------------------------
  //  Get pointers to local copy of model bounds
  //---------------------------------------------------------------------

  CoinDenseVector< double > bl(n, columnLower_);
  double *bl_elts = bl.getElements();
  CoinDenseVector< double > bu(nU, columnUpper_); // this is dummy if no UB
  double *bu_elts = bu.getElements();

  CoinDenseVector< double > r1(m, 0.0);
  double *r1_elts = r1.getElements();
  CoinDenseVector< double > x1(n, 0.0);
  double *x1_elts = x1.getElements();

  if (nfix > 0) {
    for (int k = 0; k < nfix; k++)
      x1_elts[fix[k]] = bl[fix[k]];
    matVecMult(1, r1, x1);
    b = b - r1;
    // At some stage, might want to look at normfix = norm(r1,inf);
  }

  //---------------------------------------------------------------------
  // Scale the input data.
  // The scaled variables are
  //    xbar     = x/beta,
  //    ybar     = y/zeta,
  //    zbar     = z/zeta.
  // Define
  //    theta    = beta*zeta;
  // The scaled function is
  //    phibar   = ( 1   /theta) fbar(beta*xbar),
  //    gradient = (beta /theta) grad,
  //    Hessian  = (beta2/theta) hess.
  //---------------------------------------------------------------------
  double beta = xsize_;
  if (beta == 0)
    beta = 1; // beta scales b, x.
  double zeta = zsize_;
  if (zeta == 0)
    zeta = 1; // zeta scales y, z.
  double theta = beta * zeta; // theta scales obj.
  // (theta could be anything, but theta = beta*zeta makes
  // scaled grad = grad/zeta = 1 approximately if zeta is chosen right.)

  for (int k = 0; k < nlow; k++)
    bl_elts[low[k]] = bl_elts[low[k]] / beta;
  for (int k = 0; k < nupp; k++)
    bu_elts[upp[k]] = bu_elts[upp[k]] / beta;
  d1 = d1 * (beta / sqrt(theta));
  d2 = d2 * (sqrt(theta) / beta);

  double beta2 = beta * beta;
  b.scale((1.0 / beta));
  y.scale((1.0 / zeta));
  x.scale((1.0 / beta));
  z.scale((1.0 / zeta));

  //---------------------------------------------------------------------
  // Initialize vectors that are not fully used if bounds are missing.
  //---------------------------------------------------------------------
  CoinDenseVector< double > rL(n, 0.0);
  CoinDenseVector< double > cL(n, 0.0);
  CoinDenseVector< double > z1(n, 0.0);
  CoinDenseVector< double > dx1(n, 0.0);
  CoinDenseVector< double > dz1(n, 0.0);
  CoinDenseVector< double > r2(n, 0.0);

  // Assign upper bd regions (dummy if no UBs)

  CoinDenseVector< double > rU(nU, 0.0);
  CoinDenseVector< double > cU(nU, 0.0);
  CoinDenseVector< double > x2(nU, 0.0);
  CoinDenseVector< double > z2(nU, 0.0);
  CoinDenseVector< double > dx2(nU, 0.0);
  CoinDenseVector< double > dz2(nU, 0.0);

  //---------------------------------------------------------------------
  // Initialize x, y, z, objective, etc.
  //---------------------------------------------------------------------
  CoinDenseVector< double > dx(n, 0.0);
  CoinDenseVector< double > dy(m, 0.0);
  CoinDenseVector< double > Pr(m);
  CoinDenseVector< double > D(n);
  double *D_elts = D.getElements();
  CoinDenseVector< double > w(n);
  double *w_elts = w.getElements();
  CoinDenseVector< double > rhs(m + n);

  //---------------------------------------------------------------------
  // Pull out the element array pointers for efficiency
  //---------------------------------------------------------------------
  double *x_elts = x.getElements();
  double *x2_elts = x2.getElements();
  double *z_elts = z.getElements();
  double *z1_elts = z1.getElements();
  double *z2_elts = z2.getElements();

  for (int k = 0; k < nlow; k++) {
    x_elts[low[k]] = std::max(x_elts[low[k]], bl[low[k]]);
    x1_elts[low[k]] = std::max(x_elts[low[k]] - bl[low[k]], x0min);
    z1_elts[low[k]] = std::max(z_elts[low[k]], z0min);
  }
  for (int k = 0; k < nupp; k++) {
    x_elts[upp[k]] = std::min(x_elts[upp[k]], bu[upp[k]]);
    x2_elts[upp[k]] = std::max(bu[upp[k]] - x_elts[upp[k]], x0min);
    z2_elts[upp[k]] = std::max(-z_elts[upp[k]], z0min);
  }
  //////////////////// Assume hessian is diagonal. //////////////////////

  //  [obj,grad,hess] = feval( Fname, (x*beta) );
  x.scale(beta);
  double obj = getObj(x);
  CoinDenseVector< double > grad(n);
  getGrad(x, grad);
  CoinDenseVector< double > H(n);
  getHessian(x, H);
  x.scale((1.0 / beta));

  //double * g_elts = grad.getElements();
  double *H_elts = H.getElements();

  obj /= theta; // Scaled obj.
  grad = grad * (beta / theta) + (d1 * d1) * x; // grad includes x regularization.
  H = H * (beta2 / theta) + (d1 * d1); // H    includes x regularization.

  /*---------------------------------------------------------------------
     // Compute primal and dual residuals:
     // r1 =  b - Aprod(x) - d2*d2*y;
     // r2 =  grad - Atprod(y) + z2 - z1;
     //  rL =  bl - x + x1;
     //  rU =  x + x2 - bu; */
  //---------------------------------------------------------------------
  //  [r1,r2,rL,rU,Pinf,Dinf] = ...
  //      pdxxxresid1( Aname,fix,low,upp, ...
  //                   b,bl,bu,d1,d2,grad,rL,rU,x,x1,x2,y,z1,z2 );
  pdxxxresid1(this, nlow, nupp, nfix, low, upp, fix,
    b, bl_elts, bu_elts, d1, d2, grad, rL, rU, x, x1, x2, y, z1, z2,
    r1, r2, &Pinf, &Dinf);
  //---------------------------------------------------------------------
  // Initialize mu and complementarity residuals:
  //    cL   = mu*e - X1*z1.
  //    cU   = mu*e - X2*z2.
  //
  // 25 Jan 2001: Now that b and obj are scaled (and hence x,y,z),
  //              we should be able to use mufirst = mu0 (absolute value).
  //              0.1 worked poorly on StarTest1 with x0min = z0min = 0.1.
  // 29 Jan 2001: We might as well use mu0 = x0min * z0min;
  //              so that most variables are centered after a warm start.
  // 29 Sep 2002: Use mufirst = mu0*(x0min * z0min),
  //              regarding mu0 as a scaling of the initial center.
  //---------------------------------------------------------------------
  //  double mufirst = mu0*(x0min * z0min);
  double mufirst = mu0; // revert to absolute value
  double mulast = 0.1 * opttol;
  mulast = std::min(mulast, mufirst);
  double mu = mufirst;
  double center, fmerit;
  pdxxxresid2(mu, nlow, nupp, low, upp, cL, cU, x1, x2,
    z1, z2, &center, &Cinf, &Cinf0);
  fmerit = pdxxxmerit(nlow, nupp, low, upp, r1, r2, rL, rU, cL, cU);

  // Initialize other things.

  bool precon = true;
  double PDitns = 0;
  //bool converged = false;
  double atol = atol1;
  atol2 = std::max(atol2, atolmin);
  atolmin = atol2;
  //  pdDDD2    = d2;    // Global vector for diagonal matrix D2

  //  Iteration log.

  int nf = 0;
  int itncg = 0;
  int nfail = 0;

  printf("\n\nItn   mu   stepx   stepz  Pinf  Dinf");
  printf("  Cinf   Objective    nf  center");
  if (direct) {
    printf("\n");
  } else {
    printf("  atol   solver   Inexact\n");
  }

  double regx = (d1 * x).twoNorm();
  double regy = (d2 * y).twoNorm();
  //  regterm = twoNorm(d1.*x)^2  +  norm(d2.*y)^2;
  double regterm = regx * regx + regy * regy;
  double objreg = obj + 0.5 * regterm;
  double objtrue = objreg * theta;

  printf("\n%3g                     ", PDitns);
  printf("%6.1f%6.1f", log10(Pinf), log10(Dinf));
  printf("%6.1f%15.7e", log10(Cinf0), objtrue);
  printf("   %8.1f\n", center);
  /*
     if kminor
       printf("\n\nStart of first minor itn...\n");
       keyboard
     end
     */
  //---------------------------------------------------------------------
  // Main loop.
  //---------------------------------------------------------------------
  // Lsqr
  ClpLsqr thisLsqr(this);
  //  while (converged) {
  while (PDitns < maxitn) {
    PDitns = PDitns + 1;

    // 31 Jan 2001: Set atol according to progress, a la Inexact Newton.
    // 07 Feb 2001: 0.1 not small enough for Satellite problem.  Try 0.01.
    // 25 Apr 2001: 0.01 seems wasteful for Star problem.
    //              Now that starting conditions are better, go back to 0.1.

    double r3norm = std::max(Pinf, std::max(Dinf, Cinf));
    atol = std::min(atol, r3norm * 0.1);
    atol = std::max(atol, atolmin);
    info.r3norm = r3norm;

    //-------------------------------------------------------------------
    //  Define a damped Newton iteration for solving f = 0,
    //  keeping  x1, x2, z1, z2 > 0.  We eliminate dx1, dx2, dz1, dz2
    //  to obtain the system
    //
    //     [-H2  A"  ] [ dx ] = [ w ],   H2 = H + D1^2 + X1inv Z1 + X2inv Z2,
    //     [ A   D2^2] [ dy ] = [ r1]    w  = r2 - X1inv(cL + Z1 rL)
    //                                           + X2inv(cU + Z2 rU),
    //
    //  which is equivalent to the least-squares problem
    //
    //     min || [ D A"]dy  -  [  D w   ] ||,   D = H2^{-1/2}.         (*)
    //         || [  D2 ]       [D2inv r1] ||
    //-------------------------------------------------------------------
    for (int k = 0; k < nlow; k++)
      H_elts[low[k]] = H_elts[low[k]] + z1[low[k]] / x1[low[k]];
    for (int k = 0; k < nupp; k++)
      H[upp[k]] = H[upp[k]] + z2[upp[k]] / x2[upp[k]];
    w = r2;
    for (int k = 0; k < nlow; k++)
      w[low[k]] = w[low[k]] - (cL[low[k]] + z1[low[k]] * rL[low[k]]) / x1[low[k]];
    for (int k = 0; k < nupp; k++)
      w[upp[k]] = w[upp[k]] + (cU[upp[k]] + z2[upp[k]] * rU[upp[k]]) / x2[upp[k]];

    if (LSproblem == 1) {
      //-----------------------------------------------------------------
      //  Solve (*) for dy.
      //-----------------------------------------------------------------
      H = 1.0 / H; // H is now Hinv (NOTE!)
      for (int k = 0; k < nfix; k++)
        H[fix[k]] = 0;
      for (int k = 0; k < n; k++)
        D_elts[k] = sqrt(H_elts[k]);
      thisLsqr.borrowDiag1(D_elts);
      thisLsqr.diag2_ = d2;

      if (direct) {
        // Omit direct option for now
      } else { // Iterative solve using LSQR.
        //rhs     = [ D.*w; r1./d2 ];
        for (int k = 0; k < n; k++)
          rhs[k] = D_elts[k] * w_elts[k];
        for (int k = 0; k < m; k++)
          rhs[n + k] = r1_elts[k] * (1.0 / d2);
        double damp = 0;

        if (precon) { // Construct diagonal preconditioner for LSQR
          matPrecon(d2, Pr, D);
        }
        /*
                    	rw(7)        = precon;
                            info.atolmin = atolmin;
                            info.r3norm  = fmerit;  // Must be the 2-norm here.

                            [ dy, istop, itncg, outfo ] = ...
                       pdxxxlsqr( nb,m,"pdxxxlsqrmat",Aname,rw,rhs,damp, ...
                                  atol,btol,conlim,itnlim,show,info );


                    	thisLsqr.input->rhs_vec = &rhs;
                    	thisLsqr.input->sol_vec = &dy;
                    	thisLsqr.input->rel_mat_err = atol;
                    	thisLsqr.do_lsqr(this);
                    	*/
        //  New version of lsqr

        int istop;
        dy.clear();
        show = false;
        info.atolmin = atolmin;
        info.r3norm = fmerit; // Must be the 2-norm here.

        thisLsqr.do_lsqr(rhs, damp, atol, btol, conlim, itnlim,
          show, info, dy, &istop, &itncg, &outfo, precon, Pr);
        if (precon)
          dy = dy * Pr;

        if (!precon && itncg > 999999)
          precon = true;

        if (istop == 3 || istop == 7) // conlim or itnlim
          printf("\n    LSQR stopped early:  istop = //%d", istop);

        atolold = outfo.atolold;
        atol = outfo.atolnew;
        r3ratio = outfo.r3ratio;
      } // LSproblem 1

      //      grad      = pdxxxmat( Aname,2,m,n,dy );   // grad = A"dy
      grad.clear();
      matVecMult(2, grad, dy);
      for (int k = 0; k < nfix; k++)
        grad[fix[k]] = 0; // grad is a work vector
      dx = H * (grad - w);

    } else {
      perror("This LSproblem not yet implemented\n");
    }
    //-------------------------------------------------------------------

    CGitns += itncg;

    //-------------------------------------------------------------------
    // dx and dy are now known.  Get dx1, dx2, dz1, dz2.
    //-------------------------------------------------------------------
    for (int k = 0; k < nlow; k++) {
      dx1[low[k]] = -rL[low[k]] + dx[low[k]];
      dz1[low[k]] = (cL[low[k]] - z1[low[k]] * dx1[low[k]]) / x1[low[k]];
    }
    for (int k = 0; k < nupp; k++) {
      dx2[upp[k]] = -rU[upp[k]] - dx[upp[k]];
      dz2[upp[k]] = (cU[upp[k]] - z2[upp[k]] * dx2[upp[k]]) / x2[upp[k]];
    }
    //-------------------------------------------------------------------
    // Find the maximum step.
    //--------------------------------------------------------------------
    double stepx1 = pdxxxstep(nlow, low, x1, dx1);
    double stepx2 = inf;
    if (nupp > 0)
      stepx2 = pdxxxstep(nupp, upp, x2, dx2);
    double stepz1 = pdxxxstep(z1, dz1);
    double stepz2 = inf;
    if (nupp > 0)
      stepz2 = pdxxxstep(z2, dz2);
    double stepx = std::min(stepx1, stepx2);
    double stepz = std::min(stepz1, stepz2);
    stepx = std::min(steptol * stepx, 1.0);
    stepz = std::min(steptol * stepz, 1.0);
    if (stepSame) { // For NLPs, force same step
      stepx = std::min(stepx, stepz); // (true Newton method)
      stepz = stepx;
    }

    //-------------------------------------------------------------------
    // Backtracking linesearch.
    //-------------------------------------------------------------------
    bool fail = true;
    nf = 0;

    while (nf < maxf) {
      nf = nf + 1;
      x = x + stepx * dx;
      y = y + stepz * dy;
      for (int k = 0; k < nlow; k++) {
        x1[low[k]] = x1[low[k]] + stepx * dx1[low[k]];
        z1[low[k]] = z1[low[k]] + stepz * dz1[low[k]];
      }
      for (int k = 0; k < nupp; k++) {
        x2[upp[k]] = x2[upp[k]] + stepx * dx2[upp[k]];
        z2[upp[k]] = z2[upp[k]] + stepz * dz2[upp[k]];
      }
      //      [obj,grad,hess] = feval( Fname, (x*beta) );
      x.scale(beta);
      obj = getObj(x);
      getGrad(x, grad);
      getHessian(x, H);
      x.scale((1.0 / beta));

      obj /= theta;
      grad = grad * (beta / theta) + d1 * d1 * x;
      H = H * (beta2 / theta) + d1 * d1;

      //      [r1,r2,rL,rU,Pinf,Dinf] = ...
      pdxxxresid1(this, nlow, nupp, nfix, low, upp, fix,
        b, bl_elts, bu_elts, d1, d2, grad, rL, rU, x, x1, x2,
        y, z1, z2, r1, r2, &Pinf, &Dinf);
      //double center, Cinf, Cinf0;
      //      [cL,cU,center,Cinf,Cinf0] = ...
      pdxxxresid2(mu, nlow, nupp, low, upp, cL, cU, x1, x2, z1, z2,
        &center, &Cinf, &Cinf0);
      double fmeritnew = pdxxxmerit(nlow, nupp, low, upp, r1, r2, rL, rU, cL, cU);
      double step = std::min(stepx, stepz);

      if (fmeritnew <= (1 - eta * step) * fmerit) {
        fail = false;
        break;
      }

      // Merit function didn"t decrease.
      // Restore variables to previous values.
      // (This introduces a little error, but save lots of space.)

      x = x - stepx * dx;
      y = y - stepz * dy;
      for (int k = 0; k < nlow; k++) {
        x1[low[k]] = x1[low[k]] - stepx * dx1[low[k]];
        z1[low[k]] = z1[low[k]] - stepz * dz1[low[k]];
      }
      for (int k = 0; k < nupp; k++) {
        x2[upp[k]] = x2[upp[k]] - stepx * dx2[upp[k]];
        z2[upp[k]] = z2[upp[k]] - stepz * dz2[upp[k]];
      }
      // Back-track.
      // If it"s the first time,
      // make stepx and stepz the same.

      if (nf == 1 && stepx != stepz) {
        stepx = step;
      } else if (nf < maxf) {
        stepx = stepx / 2;
      }
      stepz = stepx;
    }

    if (fail) {
      printf("\n     Linesearch failed (nf too big)");
      nfail += 1;
    } else {
      nfail = 0;
    }

    //-------------------------------------------------------------------
    // Set convergence measures.
    //--------------------------------------------------------------------
    regx = (d1 * x).twoNorm();
    regy = (d2 * y).twoNorm();
    regterm = regx * regx + regy * regy;
    objreg = obj + 0.5 * regterm;
    objtrue = objreg * theta;

    bool primalfeas = Pinf <= featol;
    bool dualfeas = Dinf <= featol;
    bool complementary = Cinf0 <= opttol;
    bool enough = PDitns >= 4; // Prevent premature termination.
    bool converged = primalfeas & dualfeas & complementary & enough;

    //-------------------------------------------------------------------
    // Iteration log.
    //-------------------------------------------------------------------
    char str1[100], str2[100], str3[100], str4[100], str5[100];
    sprintf(str1, "\n%3g%5.1f", PDitns, log10(mu));
    sprintf(str2, "%8.5f%8.5f", stepx, stepz);
    if (stepx < 0.0001 || stepz < 0.0001) {
      sprintf(str2, " %6.1e %6.1e", stepx, stepz);
    }

    sprintf(str3, "%6.1f%6.1f", log10(Pinf), log10(Dinf));
    sprintf(str4, "%6.1f%15.7e", log10(Cinf0), objtrue);
    sprintf(str5, "%3d%8.1f", nf, center);
    if (center > 99999) {
      sprintf(str5, "%3d%8.1e", nf, center);
    }
    printf("%s%s%s%s%s", str1, str2, str3, str4, str5);
    if (direct) {
      // relax
    } else {
      printf(" %5.1f%7d%7.3f", log10(atolold), itncg, r3ratio);
    }
    //-------------------------------------------------------------------
    // Test for termination.
    //-------------------------------------------------------------------
    if (kminor) {
      printf("\nStart of next minor itn...\n");
      //      keyboard;
    }

    if (converged) {
      printf("\n   Converged");
      break;
    } else if (PDitns >= maxitn) {
      printf("\n   Too many iterations");
      inform = 1;
      break;
    } else if (nfail >= maxfail) {
      printf("\n   Too many linesearch failures");
      inform = 2;
      break;
    } else {

      // Reduce mu, and reset certain residuals.

      double stepmu = std::min(stepx, stepz);
      stepmu = std::min(stepmu, steptol);
      double muold = mu;
      mu = mu - stepmu * mu;
      if (center >= bigcenter)
        mu = muold;

      // mutrad = mu0*(sum(Xz)/n); // 24 May 1998: Traditional value, but
      // mu     = std::min(mu,mutrad ); // it seemed to decrease mu too much.

      mu = std::max(mu, mulast); // 13 Jun 1998: No need for smaller mu.
      //      [cL,cU,center,Cinf,Cinf0] = ...
      pdxxxresid2(mu, nlow, nupp, low, upp, cL, cU, x1, x2, z1, z2,
        &center, &Cinf, &Cinf0);
      fmerit = pdxxxmerit(nlow, nupp, low, upp, r1, r2, rL, rU, cL, cU);

      // Reduce atol for LSQR (and SYMMLQ).
      // NOW DONE AT TOP OF LOOP.

      atolold = atol;
      // if atol > atol2
      //   atolfac = (mu/mufirst)^0.25;
      //   atol    = std::max( atol*atolfac, atol2 );
      // end

      // atol = std::min( atol, mu );     // 22 Jan 2001: a la Inexact Newton.
      // atol = std::min( atol, 0.5*mu ); // 30 Jan 2001: A bit tighter

      // If the linesearch took more than one function (nf > 1),
      // we assume the search direction needed more accuracy
      // (though this may be true only for LPs).
      // 12 Jun 1998: Ask for more accuracy if nf > 2.
      // 24 Nov 2000: Also if the steps are small.
      // 30 Jan 2001: Small steps might be ok with warm start.
      // 06 Feb 2001: Not necessarily.  Reinstated tests in next line.

      if (nf > 2 || std::min(stepx, stepz) <= 0.01)
        atol = atolold * 0.1;
    }
    //---------------------------------------------------------------------
    // End of main loop.
    //---------------------------------------------------------------------
  }

  for (int k = 0; k < nfix; k++)
    x[fix[k]] = bl[fix[k]];
  z = z1;
  if (nupp > 0)
    z = z - z2;
  printf("\n\nmax |x| =%10.3f", x.infNorm());
  printf("    max |y| =%10.3f", y.infNorm());
  printf("    max |z| =%10.3f", z.infNorm());
  printf("   scaled");

  x.scale(beta);
  y.scale(zeta);
  z.scale(zeta); // Unscale x, y, z.

  printf("\nmax |x| =%10.3f", x.infNorm());
  printf("    max |y| =%10.3f", y.infNorm());
  printf("    max |z| =%10.3f", z.infNorm());
  printf(" unscaled\n");

  time = CoinCpuTime() - time;
  char str1[100], str2[100];
  sprintf(str1, "\nPDitns  =%10g", PDitns);
  sprintf(str2, "itns =%10d", CGitns);
  //  printf( [str1 " " solver str2] );
  printf("    time    =%10.1f\n", time);
  /*
     pdxxxdistrib( abs(x),abs(z) );   // Private function

     if (wait)
       keyboard;
     */
  //-----------------------------------------------------------------------
  // End function pdco.m
  //-----------------------------------------------------------------------
  /*  printf("Solution x values:\n\n");
       for (int k=0; k<n; k++)
         printf(" %d   %e\n", k, x[k]);
     */
  // Print distribution
  double thresh[9] = { 0.00000001, 0.0000001, 0.000001, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1.00001 };
  int counts[9] = { 0 };
  for (int ij = 0; ij < n; ij++) {
    for (int j = 0; j < 9; j++) {
      if (x[ij] < thresh[j]) {
        counts[j] += 1;
        break;
      }
    }
  }
  printf("Distribution of Solution Values\n");
  for (int j = 8; j > 1; j--)
    printf(" %g  to  %g %d\n", thresh[j - 1], thresh[j], counts[j]);
  printf("   Less than   %g %d\n", thresh[2], counts[0]);

  return inform;
}
// LSQR
void ClpPdco::lsqr()
{
}

void ClpPdco::matVecMult(int mode, double *x_elts, double *y_elts)
{
  pdcoStuff_->matVecMult(this, mode, x_elts, y_elts);
}
void ClpPdco::matVecMult(int mode, CoinDenseVector< double > &x, double *y_elts)
{
  double *x_elts = x.getElements();
  matVecMult(mode, x_elts, y_elts);
  return;
}

void ClpPdco::matVecMult(int mode, CoinDenseVector< double > &x, CoinDenseVector< double > &y)
{
  double *x_elts = x.getElements();
  double *y_elts = y.getElements();
  matVecMult(mode, x_elts, y_elts);
  return;
}

void ClpPdco::matVecMult(int mode, CoinDenseVector< double > *x, CoinDenseVector< double > *y)
{
  double *x_elts = x->getElements();
  double *y_elts = y->getElements();
  matVecMult(mode, x_elts, y_elts);
  return;
}
void ClpPdco::matPrecon(double delta, double *x_elts, double *y_elts)
{
  pdcoStuff_->matPrecon(this, delta, x_elts, y_elts);
}
void ClpPdco::matPrecon(double delta, CoinDenseVector< double > &x, double *y_elts)
{
  double *x_elts = x.getElements();
  matPrecon(delta, x_elts, y_elts);
  return;
}

void ClpPdco::matPrecon(double delta, CoinDenseVector< double > &x, CoinDenseVector< double > &y)
{
  double *x_elts = x.getElements();
  double *y_elts = y.getElements();
  matPrecon(delta, x_elts, y_elts);
  return;
}

void ClpPdco::matPrecon(double delta, CoinDenseVector< double > *x, CoinDenseVector< double > *y)
{
  double *x_elts = x->getElements();
  double *y_elts = y->getElements();
  matPrecon(delta, x_elts, y_elts);
  return;
}
void ClpPdco::getBoundTypes(int *nlow, int *nupp, int *nfix, int **bptrs)
{
  *nlow = numberColumns_;
  *nupp = *nfix = 0;
  int *low_ = (int *)malloc(numberColumns_ * sizeof(int));
  for (int k = 0; k < numberColumns_; k++)
    low_[k] = k;
  bptrs[0] = low_;
  return;
}

double ClpPdco::getObj(CoinDenseVector< double > &x)
{
  return pdcoStuff_->getObj(this, x);
}

void ClpPdco::getGrad(CoinDenseVector< double > &x, CoinDenseVector< double > &g)
{
  pdcoStuff_->getGrad(this, x, g);
}

void ClpPdco::getHessian(CoinDenseVector< double > &x, CoinDenseVector< double > &H)
{
  pdcoStuff_->getHessian(this, x, H);
}

/* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
*/
